// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.expr.*;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt.*;
// import com.alibaba.druid.sql.parser.*;
// import com.alibaba.druid.util.FnvHash;

// import java.util.List;

#include "OracleSelectParser.h"
#include "OracleExprParser.h"
#include "../../../../utils/instanceof.h"
#include "../ast/stmt/OracleSelectQueryBlock.h"
#include "../ast/stmt/OracleSelectRestriction.h"
#include "../ast/stmt/OracleSelectSubqueryTableSource.h"
#include "../ast/stmt/OracleSelectTableReference.h"
#include "../ast/stmt/OracleSelectJoin.h"
#include "../../../parser/ParserException.h"
#include "../../../ast/statement/SQLWithSubqueryClause.h"
#include "../ast/clause/OracleWithSubqueryEntry.h"
#include "../ast/clause/SearchClause.h"
#include "../ast/clause/CycleClause.h"
#include "../ast/clause/ModelClause.h"
#include "../ast/clause/SampleClause.h"
#include "../ast/clause/PartitionExtensionClause.h"
#include "../../../../utils/FnvHash.h"
#include "../../../../utils/StringUtils.h"
#include "../../../ast/expr/SQLIdentifierExpr.h"
#include "../../../ast/expr/SQLFlashbackExpr.h"

OracleSelectParser::OracleSelectParser(string_ptr sql)
    : SQLSelectParser(OracleExprParser_ptr(new OracleExprParser(sql)))
{
}

OracleSelectParser::OracleSelectParser(SQLExprParser_ptr exprParser)
    : SQLSelectParser(exprParser)
{
}

OracleSelectParser::OracleSelectParser(SQLExprParser_ptr exprParser, SQLSelectListCache_ptr selectListCache)
    : SQLSelectParser(exprParser, selectListCache)
{
}

SQLSelect_ptr OracleSelectParser::select()
{
  SQLSelect_ptr select = SQLSelect_ptr(new SQLSelect());

  if (lexer->token()->name->c_str() == Token::WITH.name->c_str())
  {
    SQLWithSubqueryClause_ptr with = this->parseWith();
    select->setWithSubQuery(with);
  }

  SQLSelectQuery_ptr query_ = SQLSelectParser::query();
  select->setQuery(query_);

  SQLOrderBy_ptr orderBy = this->parseOrderBy();

  OracleSelectQueryBlock_ptr queryBlock = nullptr;
  if (instanceof <SQLSelectQuery, SQLSelectQueryBlock>(query_))
  {
    queryBlock = std::dynamic_pointer_cast<OracleSelectQueryBlock>(query_);

    if (queryBlock->getOrderBy() == nullptr)
    {
      queryBlock->setOrderBy(orderBy);
    }
    else
    {
      select->setOrderBy(orderBy);
    }

    if (orderBy != nullptr)
    {
      parseFetchClause(queryBlock);

      select->setQuery(this->queryRest(queryBlock, BOOL::TRUE));
    }
  }
  else
  {
    select->setOrderBy(orderBy);
  }

  if (lexer->token()->name->c_str() == (Token::FOR.name->c_str()))
  {
    if (queryBlock == nullptr)
    {
      std::string tmp = "TODO. ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    lexer->nextToken();
    accept(Token::UPDATE);

    queryBlock->setForUpdate(BOOL::TRUE);

    // OracleSelectForUpdate forUpdate = new OracleSelectForUpdate();

    if (lexer->token()->name->c_str() == Token::OF.name->c_str())
    {
      lexer->nextToken();
      this->exprParser->exprList(queryBlock->getForUpdateOf(), queryBlock);
    }
    else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str() && queryBlock->isForUpdate())
    {
      this->exprParser->exprList(queryBlock->getForUpdateOf(), queryBlock);
    }

    if (lexer->token()->name->c_str() == Token::NOWAIT.name->c_str())
    {
      lexer->nextToken();
      queryBlock->setNoWait(BOOL::TRUE);
    }
    else if (lexer->token()->name->c_str() == Token::WAIT.name->c_str())
    {
      lexer->nextToken();
      queryBlock->setWaitTime(this->exprParser->primary());
    }
    else if (lexer->identifierEquals(make_string_ptr("SKIP")))
    {
      lexer->nextToken();
      acceptIdentifier("LOCKED");
      queryBlock->setSkipLocked(BOOL::TRUE);
    }
  }

  if (lexer->token()->name->c_str() == Token::ORDER.name->c_str())
  {
    orderBy = this->exprParser->parseOrderBy();
    if (queryBlock != nullptr && queryBlock->getOrderBy() == nullptr)
    {
      queryBlock->setOrderBy(orderBy);
    }
    else if (select->getOrderBy() == nullptr)
    {
      select->setOrderBy(orderBy);
    }
    else
    {
      throw new ParserException(make_string_ptr("illegal state."));
    }
  }

  if (lexer->token()->name->c_str() == Token::WITH.name->c_str())
  {
    lexer->nextToken();

    OracleSelectRestriction_ptr restriction = nullptr;
    if (lexer->identifierEquals(make_string_ptr("READ")))
    {
      lexer->nextToken();

      if (lexer->identifierEquals(make_string_ptr("ONLY")))
      {
        lexer->nextToken();
      }
      else
      {
        std::string tmp = "syntax error. ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }

      restriction = OracleSelectRestriction_ReadOnly_ptr(new OracleSelectRestriction_ReadOnly());
    }
    else if (lexer->token()->name->c_str() == (Token::CHECK.name->c_str()))
    {
      lexer->nextToken();

      if (lexer->identifierEquals(make_string_ptr("OPTION")))
      {
        lexer->nextToken();
      }
      else
      {
        std::string tmp = "syntax error. ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }

      restriction = OracleSelectRestriction_CheckOption_ptr(new OracleSelectRestriction_CheckOption());
    }
    else
    {
      std::string tmp = "syntax error. ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    if (lexer->token()->name->c_str() == Token::CONSTRAINT.name->c_str())
    {
      lexer->nextToken();
      string_ptr constraintName = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      SQLName_ptr constraint = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(constraintName));
      restriction->setConstraint(constraint);

      lexer->nextToken();
    }

    select->setRestriction(restriction);
  }

  return select;
}

// @Override
SQLWithSubqueryClause_ptr OracleSelectParser::parseWith()
{
  accept(Token::WITH);
  SQLWithSubqueryClause_ptr subqueryFactoringClause = SQLWithSubqueryClause_ptr(new SQLWithSubqueryClause());
  for (;;)
  {
    OracleWithSubqueryEntry_ptr entry = OracleWithSubqueryEntry_ptr(new OracleWithSubqueryEntry());

    string_ptr alias = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();
    entry->setAlias(alias);

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      exprParser->names(entry->getColumns());
      accept(Token::RPAREN);
    }

    accept(Token::AS);
    accept(Token::LPAREN);
    entry->setSubQuery(select());
    accept(Token::RPAREN);

    if (lexer->identifierEquals(make_string_ptr("SEARCH")))
    {
      lexer->nextToken();
      SearchClause_ptr searchClause = SearchClause_ptr(new SearchClause());

      if (lexer->token()->name->c_str() != Token::IDENTIFIER.name->c_str())
      {
        std::string tmp = "syntax error. ";
        tmp += lexer->token()->name->c_str();
        throw new ParserException(make_string_ptr(tmp));
      }

      if (lexer->identifierEquals(FnvHash::Constants::DEPTH))
      {
        lexer->nextToken();
        searchClause->setType(SearchClause_Type::DEPTH);
      }
      else if (lexer->identifierEquals(FnvHash::Constants::BREADTH))
      {
        lexer->nextToken();
        searchClause->setType(SearchClause_Type::BREADTH);
      }
      else
      {
        searchClause->setType(SearchClause_Type::valueOf(StringUtils::toUpper(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())))));
        lexer->nextToken();
      }

      acceptIdentifier("FIRST");
      accept(Token::BY);

      searchClause->addItem(exprParser->parseSelectOrderByItem());

      while (lexer->token()->name->c_str() == (Token::COMMA.name->c_str()))
      {
        lexer->nextToken();
        searchClause->addItem(exprParser->parseSelectOrderByItem());
      }

      accept(Token::SET);

      searchClause->setOrderingColumn(std::dynamic_pointer_cast<SQLIdentifierExpr>(exprParser->name()));

      entry->setSearchClause(searchClause);
    }

    if (lexer->identifierEquals(make_string_ptr("CYCLE")))
    {
      lexer->nextToken();
      CycleClause_ptr cycleClause = CycleClause_ptr(new CycleClause());
      exprParser->exprList(cycleClause->getAliases(), cycleClause);
      accept(Token::SET);
      cycleClause->setMark(exprParser->expr());
      accept(Token::TO);
      cycleClause->setValue(exprParser->expr());
      accept(Token::DEFAULT);
      cycleClause->setDefaultValue(exprParser->expr());
      entry->setCycleClause(cycleClause);
    }

    subqueryFactoringClause->addEntry(entry);

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      continue;
    }

    break;
  }

  return subqueryFactoringClause;
}

SQLSelectQuery_ptr OracleSelectParser::query(SQLObject_ptr parent, BOOL_ptr acceptUnion)
{
  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    SQLSelectQuery_ptr select = SQLSelectParser::query();
    accept(Token::RPAREN);

    return queryRest(select, acceptUnion);
  }

  OracleSelectQueryBlock_ptr queryBlock = OracleSelectQueryBlock_ptr(new OracleSelectQueryBlock());
  if (lexer->hasComment() && lexer->isKeepComments())
  {
    queryBlock->addBeforeComment(lexer->readAndResetComments());
  }

  if (lexer->token()->name->c_str() == Token::SELECT.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::COMMENT.name->c_str())
    {
      lexer->nextToken();
    }

    parseHints(queryBlock);

    if (lexer->token()->name->c_str() == Token::DISTINCT.name->c_str())
    {
      queryBlock->setDistionOption(SQLSetQuantifier::DISTINCT);
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::UNIQUE.name->c_str())
    {
      queryBlock->setDistionOption(SQLSetQuantifier::UNIQUE);
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::ALL.name->c_str())
    {
      queryBlock->setDistionOption(SQLSetQuantifier::ALL);
      lexer->nextToken();
    }

    this->exprParser->parseHints(queryBlock->getHints());

    parseSelectList(queryBlock);
  }

  parseInto(queryBlock);

  parseFrom(queryBlock);

  parseWhere(queryBlock);

  parseHierachical(queryBlock);

  parseGroupBy(queryBlock);

  parseModelClause(queryBlock);

  parseFetchClause(queryBlock);

  return queryRest(queryBlock, acceptUnion);
}

SQLSelectQuery_ptr OracleSelectParser::queryRest(SQLSelectQuery_ptr selectQuery, BOOL_ptr acceptUnion)
{
  if (!acceptUnion)
  {
    return selectQuery;
  }

  if (lexer->token()->name->c_str() == Token::UNION.name->c_str())
  {
    do
    {
      SQLUnionQuery_ptr union_ = SQLUnionQuery_ptr(new SQLUnionQuery());
      union_->setLeft(selectQuery);

      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::ALL.name->c_str())
      {
        union_->setOperator(SQLUnionOperator::UNION_ALL);
        lexer->nextToken();
      }
      else if (lexer->token()->name->c_str() == Token::DISTINCT.name->c_str())
      {
        union_->setOperator(SQLUnionOperator::DISTINCT);
        lexer->nextToken();
      }

      SQLSelectQuery_ptr right = query(nullptr, BOOL::FALSE);

      union_->setRight(right);

      selectQuery = union_;

    } while (lexer->token()->name->c_str() == Token::UNION.name->c_str());

    selectQuery = queryRest(selectQuery, BOOL::TRUE);

    return selectQuery;
  }

  if (lexer->token()->name->c_str() == Token::INTERSECT.name->c_str())
  {
    lexer->nextToken();

    SQLUnionQuery_ptr union_ = SQLUnionQuery_ptr(new SQLUnionQuery());
    union_->setLeft(selectQuery);

    union_->setOperator(SQLUnionOperator::INTERSECT);

    SQLSelectQuery_ptr right = this->query(nullptr, BOOL::FALSE);
    union_->setRight(right);

    return queryRest(union_, BOOL::TRUE);
  }

  if (lexer->token()->name->c_str() == Token::MINUS.name->c_str())
  {
    lexer->nextToken();

    SQLUnionQuery_ptr union_ = SQLUnionQuery_ptr(new SQLUnionQuery());
    union_->setLeft(selectQuery);

    union_->setOperator(SQLUnionOperator::MINUS);

    SQLSelectQuery_ptr right = this->query(nullptr, BOOL::FALSE);
    union_->setRight(right);

    return queryRest(union_, BOOL::TRUE);
  }

  return selectQuery;
}

void OracleSelectParser::parseModelClause(OracleSelectQueryBlock_ptr queryBlock)
{
  Lexer_SavePoint_ptr savePoint = lexer->mark();

  if (!lexer->identifierEquals(FnvHash::Constants::MODEL))
  {
    return;
  }

  lexer->nextToken();

  ModelClause_ptr model = ModelClause_ptr(new ModelClause());
  parseCellReferenceOptions(model->getCellReferenceOptions());

  if (lexer->identifierEquals(FnvHash::Constants::RETURN))
  {
    lexer->nextToken();
    ModelClause_ReturnRowsClause_ptr returnRowsClause = ModelClause_ReturnRowsClause_ptr(new ModelClause_ReturnRowsClause());
    if (lexer->token()->name->c_str() == Token::ALL.name->c_str())
    {
      lexer->nextToken();
      returnRowsClause->setAll(BOOL::TRUE);
    }
    else
    {
      acceptIdentifier("UPDATED");
    }
    acceptIdentifier("ROWS");

    model->setReturnRowsClause(returnRowsClause);
  }

  while (lexer->identifierEquals(FnvHash::Constants::REFERENCE))
  {
    ModelClause_ReferenceModelClause_ptr referenceModelClause = ModelClause_ReferenceModelClause_ptr(new ModelClause_ReferenceModelClause());
    lexer->nextToken();

    SQLExpr_ptr name = expr();
    referenceModelClause->setName(name);

    accept(Token::ON);
    accept(Token::LPAREN);
    SQLSelect_ptr subQuery = this->select();
    accept(Token::RPAREN);
    referenceModelClause->setSubQuery(subQuery);

    parseModelColumnClause(referenceModelClause);

    parseCellReferenceOptions(referenceModelClause->getCellReferenceOptions());

    model->getReferenceModelClauses()->push_back(referenceModelClause);
  }

  parseMainModelClause(model);

  queryBlock->setModelClause(model);
}

void OracleSelectParser::parseMainModelClause(ModelClause_ptr modelClause)
{
  ModelClause_MainModelClause_ptr mainModel = ModelClause_MainModelClause_ptr(new ModelClause_MainModelClause());

  if (lexer->identifierEquals(make_string_ptr("MAIN")))
  {
    lexer->nextToken();
    mainModel->setMainModelName(expr());
  }

  ModelClause_ModelColumnClause_ptr modelColumnClause = ModelClause_ModelColumnClause_ptr(new ModelClause_ModelColumnClause());
  parseQueryPartitionClause(modelColumnClause);
  mainModel->setModelColumnClause(modelColumnClause);

  acceptIdentifier("DIMENSION");
  accept(Token::BY);
  accept(Token::LPAREN);
  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
    {
      lexer->nextToken();
      break;
    }

    ModelClause_ModelColumn_ptr column = ModelClause_ModelColumn_ptr(new ModelClause_ModelColumn());
    column->setExpr(expr());
    column->setAlias(as());
    modelColumnClause->getDimensionByColumns()->push_back(column);

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      continue;
    }
  }

  acceptIdentifier("MEASURES");
  accept(Token::LPAREN);
  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
    {
      lexer->nextToken();
      break;
    }

    ModelClause_ModelColumn_ptr column = ModelClause_ModelColumn_ptr(new ModelClause_ModelColumn());
    column->setExpr(expr());
    column->setAlias(as());
    modelColumnClause->getMeasuresColumns()->push_back(column);

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      continue;
    }
  }
  mainModel->setModelColumnClause(modelColumnClause);

  parseCellReferenceOptions(mainModel->getCellReferenceOptions());

  parseModelRulesClause(mainModel);

  modelClause->setMainModel(mainModel);
}

void OracleSelectParser::parseModelRulesClause(ModelClause_MainModelClause_ptr mainModel)
{
  ModelClause_ModelRulesClause_ptr modelRulesClause = ModelClause_ModelRulesClause_ptr(new ModelClause_ModelRulesClause());
  if (lexer->identifierEquals(make_string_ptr("RULES")))
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
    {
      modelRulesClause->getOptions()->push_back(ModelClause_ModelRuleOption::UPDATE);
      lexer->nextToken();
    }
    else if (lexer->identifierEquals(make_string_ptr("UPSERT")))
    {
      modelRulesClause->getOptions()->push_back(ModelClause_ModelRuleOption::UPSERT);
      lexer->nextToken();
    }

    if (lexer->identifierEquals(make_string_ptr("AUTOMATIC")))
    {
      lexer->nextToken();
      accept(Token::ORDER);
      modelRulesClause->getOptions()->push_back(ModelClause_ModelRuleOption::AUTOMATIC_ORDER);
    }
    else if (lexer->identifierEquals(make_string_ptr("SEQUENTIAL")))
    {
      lexer->nextToken();
      accept(Token::ORDER);
      modelRulesClause->getOptions()->push_back(ModelClause_ModelRuleOption::SEQUENTIAL_ORDER);
    }
  }

  if (lexer->identifierEquals(make_string_ptr("ITERATE")))
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    modelRulesClause->setIterate(expr());
    accept(Token::RPAREN);

    if (lexer->identifierEquals(make_string_ptr("UNTIL")))
    {
      lexer->nextToken();
      accept(Token::LPAREN);
      modelRulesClause->setUntil(expr());
      accept(Token::RPAREN);
    }
  }

  accept(Token::LPAREN);
  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
    {
      lexer->nextToken();
      break;
    }

    ModelClause_CellAssignmentItem_ptr item = ModelClause_CellAssignmentItem_ptr(new ModelClause_CellAssignmentItem());
    if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
    {
      item->setOption(ModelClause_ModelRuleOption::UPDATE);
    }
    else if (lexer->identifierEquals(make_string_ptr("UPSERT")))
    {
      item->setOption(ModelClause_ModelRuleOption::UPSERT);
    }

    item->setCellAssignment(parseCellAssignment());
    item->setOrderBy(this->parseOrderBy());
    accept(Token::EQ);

    SQLExpr_ptr expr = this->expr();
    item->setExpr(expr);

    modelRulesClause->getCellAssignmentItems()->push_back(item);

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      continue;
    }
  }

  mainModel->setModelRulesClause(modelRulesClause);
}

ModelClause_CellAssignment_ptr OracleSelectParser::parseCellAssignment()
{
  ModelClause_CellAssignment_ptr cellAssignment = ModelClause_CellAssignment_ptr(new ModelClause_CellAssignment());

  cellAssignment->setMeasureColumn(this->exprParser->name());
  accept(Token::LBRACKET);
  this->exprParser->exprList(cellAssignment->getConditions(), cellAssignment);
  accept(Token::RBRACKET);

  return cellAssignment;
}

void OracleSelectParser::parseQueryPartitionClause(ModelClause_ModelColumnClause_ptr modelColumnClause)
{
  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    ModelClause_QueryPartitionClause_ptr queryPartitionClause = ModelClause_QueryPartitionClause_ptr(new ModelClause_QueryPartitionClause());

    lexer->nextToken();
    accept(Token::BY);
    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      exprParser->exprList(queryPartitionClause->getExprList(), queryPartitionClause);
      accept(Token::RPAREN);
    }
    else
    {
      exprParser->exprList(queryPartitionClause->getExprList(), queryPartitionClause);
    }
    modelColumnClause->setQueryPartitionClause(queryPartitionClause);
  }
}

void OracleSelectParser::parseModelColumnClause(ModelClause_ReferenceModelClause_ptr referenceModelClause)
{
  throw new ParserException();
}

void OracleSelectParser::parseCellReferenceOptions(ModelClause_CellReferenceOption_list_ptr options)
{
  if (lexer->identifierEquals(FnvHash::Constants::IGNORE))
  {
    lexer->nextToken();
    acceptIdentifier("NAV");
    options->push_back(ModelClause_CellReferenceOption::IgnoreNav);
  }
  else if (lexer->identifierEquals(FnvHash::Constants::KEEP))
  {
    lexer->nextToken();
    acceptIdentifier("NAV");
    options->push_back(ModelClause_CellReferenceOption::KeepNav);
  }

  if (lexer->token()->name->c_str() == Token::UNIQUE.name->c_str())
  {
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("DIMENSION")))
    {
      lexer->nextToken();
      options->push_back(ModelClause_CellReferenceOption::UniqueDimension);
    }
    else
    {
      acceptIdentifier("SINGLE");
      acceptIdentifier("REFERENCE");
      options->push_back(ModelClause_CellReferenceOption::UniqueDimension);
    }
  }
}

// @Override
SQLTableSource_ptr OracleSelectParser::parseTableSource()
{
  SQLTableSource_ptr tableSource = parseTableSourcePrimary();

  if (instanceof <SQLTableSource, OracleSelectTableSource>(tableSource))
  {
    return parseTableSourceRest(std::dynamic_pointer_cast<OracleSelectTableSource>(tableSource));
  }

  return SQLSelectParser::parseTableSourceRest(tableSource);
}

SQLTableSource_ptr OracleSelectParser::parseTableSourcePrimary()
{
  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    OracleSelectTableSource_ptr tableSource;
    if (lexer->token()->name->c_str() == Token::SELECT.name->c_str() ||
        lexer->token()->name->c_str() == Token::WITH.name->c_str())
    {
      tableSource = OracleSelectSubqueryTableSource_ptr(new OracleSelectSubqueryTableSource(select()));
    }
    else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      tableSource = std::dynamic_pointer_cast<OracleSelectTableSource>(parseTableSource());
    }
    else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() || lexer->token()->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
    {
      SQLTableSource_ptr identTable = parseTableSource();
      accept(Token::RPAREN);
      parsePivot(identTable);
      return identTable;
    }
    else
    {

      std::string tmp = "TODO :";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    accept(Token::RPAREN);

    if ((lexer->token()->name->c_str() == Token::UNION.name->c_str() ||
         lexer->token()->name->c_str() == Token::MINUS.name->c_str() ||
         lexer->token()->name->c_str() == Token::EXCEPT.name->c_str()) &&
        instanceof <OracleSelectTableSource, OracleSelectSubqueryTableSource>(tableSource))
    {
      OracleSelectSubqueryTableSource_ptr selectSubqueryTableSource = std::dynamic_pointer_cast<OracleSelectSubqueryTableSource>(tableSource);
      SQLSelect_ptr select = selectSubqueryTableSource->getSelect();
      SQLSelectQuery_ptr selectQuery = this->queryRest(select->getQuery(), BOOL::TRUE);
      select->setQuery(selectQuery);
    }

    parsePivot(tableSource);

    return tableSource;
  }

  if (lexer->token()->name->c_str() == Token::SELECT.name->c_str())
  {
    std::string tmp = "TODO :";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }

  OracleSelectTableReference_ptr tableReference = OracleSelectTableReference_ptr(new OracleSelectTableReference());

  if (lexer->identifierEquals(make_string_ptr("ONLY")))
  {
    lexer->nextToken();
    tableReference->setOnly(BOOL::TRUE);
    accept(Token::LPAREN);
    parseTableSourceQueryTableExpr(tableReference);
    accept(Token::RPAREN);
  }
  else
  {
    parseTableSourceQueryTableExpr(tableReference);
    parsePivot(tableReference);
  }

  return tableReference;
}

void OracleSelectParser::parseTableSourceQueryTableExpr(OracleSelectTableReference_ptr tableReference)
{
  tableReference->setExpr(this->exprParser->expr());

  //        {
  //            FlashbackQueryClause clause = flashback();
  //            tableReference.setFlashback(clause);
  //        }

  if (lexer->identifierEquals(make_string_ptr("SAMPLE")))
  {
    lexer->nextToken();

    SampleClause_ptr sample = SampleClause_ptr(new SampleClause());

    if (lexer->identifierEquals(make_string_ptr("BLOCK")))
    {
      sample->setBlock(BOOL::TRUE);
      lexer->nextToken();
    }

    accept(Token::LPAREN);
    this->exprParser->exprList(sample->getPercent(), sample);
    accept(Token::RPAREN);

    if (lexer->identifierEquals(make_string_ptr("SEED")))
    {
      lexer->nextToken();
      accept(Token::LPAREN);
      sample->setSeedValue(expr());
      accept(Token::RPAREN);
    }

    tableReference->setSampleClause(sample);
  }

  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    lexer->nextToken();
    PartitionExtensionClause_ptr partition = PartitionExtensionClause_ptr(new PartitionExtensionClause());

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      partition->setPartition(exprParser->name());
      accept(Token::RPAREN);
    }
    else if (lexer->token()->name->c_str() == Token::BY.name->c_str())
    {
      lexer->nextToken();
      accept(Token::LPAREN);
      partition->setPartition(exprParser->name());
      accept(Token::RPAREN);
    }
    else
    {
      accept(Token::FOR);
      accept(Token::LPAREN);
      exprParser->names(partition->getFor());
      accept(Token::RPAREN);
    }

    tableReference->setPartition(partition);
  }

  if (lexer->identifierEquals(make_string_ptr("SUBPARTITION")))
  {
    lexer->nextToken();
    PartitionExtensionClause_ptr partition = PartitionExtensionClause_ptr(new PartitionExtensionClause());
    partition->setSubPartition(BOOL::TRUE);

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      partition->setPartition(exprParser->name());
      accept(Token::RPAREN);
    }
    else
    {
      accept(Token::FOR);
      accept(Token::LPAREN);
      exprParser->names(partition->getFor());
      accept(Token::RPAREN);
    }

    tableReference->setPartition(partition);
  }

  if (lexer->identifierEquals(make_string_ptr("VERSIONS")))
  {
    SQLBetweenExpr_ptr betweenExpr = SQLBetweenExpr_ptr(new SQLBetweenExpr());
    betweenExpr->setTestExpr(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("VERSIONS"))));
    lexer->nextToken();

    accept(Token::BETWEEN);

    SQLFlashbackExpr_ptr start = SQLFlashbackExpr_ptr(new SQLFlashbackExpr());
    if (lexer->identifierEquals(make_string_ptr("SCN")))
    {
      lexer->nextToken();
      start->setType(SQLFlashbackExpr_Type::SCN);
    }
    else
    {
      acceptIdentifier("TIMESTAMP");
      start->setType(SQLFlashbackExpr_Type::TIMESTAMP);
    }

    SQLBinaryOpExpr_ptr binaryExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(exprParser->expr());
    if (binaryExpr->getOperator()->name->c_str() != SQLBinaryOperator::BooleanAnd.name->c_str())
    {
      std::string tmp = "syntax error. ";
      tmp += binaryExpr->getOperator()->name->c_str();
      tmp += ", ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    start->setExpr(binaryExpr->getLeft());

    betweenExpr->setBeginExpr(start);
    betweenExpr->setEndExpr(binaryExpr->getRight());

    tableReference->setFlashback(betweenExpr);
  }
}

SQLExpr_ptr OracleSelectParser::flashback()
{
  accept(Token::OF);
  if (lexer->identifierEquals(make_string_ptr("SCN")))
  {
    lexer->nextToken();
    return SQLFlashbackExpr_ptr(new SQLFlashbackExpr(SQLFlashbackExpr_Type::SCN, this->expr()));
  }
  else if (lexer->identifierEquals(make_string_ptr("SNAPSHOT")))
  {
    return this->expr();
  }
  else
  {
    lexer->nextToken();
    return SQLFlashbackExpr_ptr(new SQLFlashbackExpr(SQLFlashbackExpr_Type::TIMESTAMP, this->expr()));
  }
}

SQLTableSource_ptr OracleSelectParser::primaryTableSourceRest(SQLTableSource_ptr tableSource)
{
  if (instanceof <SQLTableSource, OracleSelectTableSource>(tableSource))
  {
    if (lexer->token()->name->c_str() == Token::AS.name->c_str())
    {
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::OF.name->c_str())
      {
        (std::dynamic_pointer_cast<OracleSelectTableSource>(tableSource))->setFlashback(flashback());
      }

      tableSource->setAlias(tableAlias());
    }
  }

  return tableSource;
}

SQLTableSource_ptr OracleSelectParser::parseTableSourceRest(OracleSelectTableSource_ptr tableSource)
{
  if (lexer->token()->name->c_str() == Token::AS.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::OF.name->c_str())
    {
      tableSource->setFlashback(flashback());
      return parseTableSourceRest(tableSource);
    }

    tableSource->setAlias(tableAlias(BOOL::TRUE));
  }
  else if ((tableSource->getAlias() == nullptr) || (tableSource->getAlias()->length() == 0))
  {
    if (lexer->token()->name->c_str() != Token::LEFT.name->c_str() &&
        lexer->token()->name->c_str() != Token::RIGHT.name->c_str() &&
        lexer->token()->name->c_str() != Token::FULL.name->c_str())
    {
      string_ptr tableAlias = SQLParser::tableAlias();
      tableSource->setAlias(tableAlias);
    }
  }

  if (lexer->token()->name->c_str() == Token::HINT.name->c_str())
  {
    this->exprParser->parseHints(tableSource->getHints());
  }

  SQLJoinTableSource_JoinType_ptr joinType = nullptr;

  if (lexer->token()->name->c_str() == Token::LEFT.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::OUTER.name->c_str())
    {
      lexer->nextToken();
    }
    accept(Token::JOIN);
    joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::LEFT_OUTER_JOIN));
  }

  if (lexer->token()->name->c_str() == Token::RIGHT.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::OUTER.name->c_str())
    {
      lexer->nextToken();
    }
    accept(Token::JOIN);
    joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::RIGHT_OUTER_JOIN));
  }

  if (lexer->token()->name->c_str() == Token::FULL.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::OUTER.name->c_str())
    {
      lexer->nextToken();
    }
    accept(Token::JOIN);
    joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::FULL_OUTER_JOIN));
  }

  bool natural = lexer->identifierEquals(FnvHash::Constants::NATURAL);
  if (natural)
  {
    lexer->nextToken();
  }

  if (lexer->token()->name->c_str() == Token::INNER.name->c_str())
  {
    lexer->nextToken();
    accept(Token::JOIN);
    if (natural)
    {
      joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::NATURAL_INNER_JOIN));
    }
    else
    {
      joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::INNER_JOIN));
    }
  }
  if (lexer->token()->name->c_str() == Token::CROSS.name->c_str())
  {
    lexer->nextToken();
    accept(Token::JOIN);
    joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::CROSS_JOIN));
  }

  if (lexer->token()->name->c_str() == Token::JOIN.name->c_str())
  {
    lexer->nextToken();
    if (natural)
    {
      joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::NATURAL_JOIN));
    }
    else
    {
      joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::JOIN));
    }
  }

  if (lexer->token()->name->c_str() == (Token::COMMA.name->c_str()))
  {
    lexer->nextToken();
    joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::COMMA));
  }

  if (joinType != nullptr)
  {
    OracleSelectJoin_ptr join = OracleSelectJoin_ptr(new OracleSelectJoin());
    join->SQLJoinTableSource::setLeft(tableSource);
    join->setJoinType(joinType);

    SQLTableSource_ptr right;
    right = parseTableSourcePrimary();
    string_ptr tableAlias_ = tableAlias();
    right->setAlias(tableAlias_);
    join->SQLJoinTableSource::setRight(right);

    if (lexer->token()->name->c_str() == Token::ON.name->c_str())
    {
      lexer->nextToken();
      join->setCondition(this->exprParser->expr());

      if (lexer->token()->name->c_str() == Token::ON.name->c_str() && instanceof <OracleSelectTableSource, SQLJoinTableSource>(tableSource) && (std::dynamic_pointer_cast<SQLJoinTableSource>(tableSource))->getCondition() == nullptr)
      {
        lexer->nextToken();
        SQLExpr_ptr leftCondidition = this->exprParser->expr();
        (std::dynamic_pointer_cast<SQLJoinTableSource>(tableSource))->setCondition(leftCondidition);
      }
    }
    else if (lexer->token()->name->c_str() == Token::USING.name->c_str())
    {
      lexer->nextToken();
      accept(Token::LPAREN);
      this->exprParser->exprList(join->getUsing(), join);
      accept(Token::RPAREN);
    }

    parsePivot(join);

    return parseTableSourceRest(join);
  }
  else
  {
    if (lexer->identifierEquals(FnvHash::Constants::PIVOT))
    {
      parsePivot(tableSource);
    }
  }

  return tableSource;
}

void OracleSelectParser::parseInto(OracleSelectQueryBlock_ptr x)
{
  if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::FROM.name->c_str())
    {
      return;
    }

    SQLExpr_ptr expr_ = expr();
    if (lexer->token()->name->c_str() != Token::COMMA.name->c_str())
    {
      x->setInto(expr_);
      return;
    }
    SQLListExpr_ptr list = SQLListExpr_ptr(new SQLListExpr());
    list->addItem(expr_);
    while (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      list->addItem(expr());
    }
    x->setInto(list);
  }
}

void OracleSelectParser::parseHints(OracleSelectQueryBlock_ptr queryBlock)
{
  this->exprParser->parseHints(queryBlock->getHints());
}
